import os
import openai
import requests
import json

from IPython.display import Image, display
from dashscope import ImageSynthesis
from utils import save_file, load_config, read_text_from_file

# 加载配置文件代码在 utils.load_config
project_config = load_config("config.json")

client = openai.OpenAI(
    api_key=os.getenv("DASHSCOPE_API_KEY"),
    base_url="https://dashscope.aliyuncs.com/compatible-mode/v1",
)

# 读取配置文件中的课程内容信息
title = project_config["title"]

print('输出config取title为', title)



def generate_illustration(prompt, illustration_number, illustration_title):
    """
    根据给定提示生成插图，并将其保存为文件。

    :param prompt: str，描述生成图像的提示信息。
    :param illustration_number: int，指明插图的顺序编号，用于命名文件。
    :param illustration_title: str，插图的标题，用于命名文件。

    :return: str或None，返回生成的图像文件路径；如果生成或保存失败，返回 None。
    """

    # 调用生成图像的函数，使用指定的模型并设置生成图像的大小
    rsp = ImageSynthesis.call(model="flux-merged",
                               prompt=prompt,
                               size='1024*1024')

    # 创建保存图像的目录
    output_dir = project_config["illustrations_file_folder"]
    os.makedirs(output_dir, exist_ok=True)  # 确保目录存在，以便保存生成的图像

    # 获取项目的标题用于命名文件
    title = project_config["title"]

    # 检查生成结果是否为空
    if not rsp.output.results:
        print(f"生成插图失败: {illustration_title}, 返回结果为空")
        return None  # 返回 None，表示生成失败

    # 假设处理第一张生成的插图
    result = rsp.output.results[0]  # 获取生成的第一张图像的结果
    # 构造保存文件的名称
    file_name = f"{title}_{illustration_number}_{illustration_title}.png"
    # 使用指定的输出目录和文件名构建完整的文件路径
    file_path = os.path.join(output_dir, file_name)

    # 尝试下载并保存生成的图像
    try:
        with open(file_path, 'wb+') as f:
            f.write(requests.get(result.url).content)  # 从 URL 下载图像内容并写入文件
    except Exception as e:
        print(f"保存图像失败: {file_name}, 错误信息: {e}")
        return None  # 返回 None，表示保存失败

    return file_path  # 返回生成的图片文件路径

def generate_illustrations(illustrations):
    """
    生成插图并返回更新后的插图信息的 JSON 格式文本。

    :param illustrations: JSON 字符串或包含插图信息的列表
    :return: 返回更新后的插图信息的 JSON 文本
    """
    output_dir = project_config["illustrations_file_folder"]

    # 检查输入类型
    if isinstance(illustrations, str):
        illustrations = json.loads(illustrations)
    elif not isinstance(illustrations, list):
        raise ValueError("illustrations 需要是一个 JSON 字符串或列表")

    results = []

    # 批量调用生成插图的功能
    for item in illustrations:
        illustration_number = item['插图编号']
        illustration_location = item['插图位置']
        illustration_title = item['图片标题']
        prompt = item['插图描述']

        # 调用生成插图的函数
        print(f'正在生成插图，编号: {illustration_number}，提示词：{prompt}')
        image_path = generate_illustration(prompt, illustration_number, illustration_title)

        # 检查生成插图是否成功
        if image_path:  # 如果生成成功
            print(f"生成插图成功: 编号: {illustration_number}, 标题: {illustration_title}, 保存路径: {image_path}")
            # 在 notebook 中显示图片，生产环境可能需要修改为保存到对象存储 OSS 等存储介质中
            display(Image(filename=image_path, width=128))
            # 更新插图路径，直接使用生成的完整路径
            item['插图路径'] = image_path  # 直接赋值生成的路径

            # 将更新后的插图信息添加到结果列表
            results.append(item)
        else:
            print(f"插图生成失败: 编号: {illustration_number}, 标题: {illustration_title}")

    # 将结果列表转换为 JSON 格式的字符串
    final_result = json.dumps(results, ensure_ascii=False, indent=4)  # 确保中文字符正常显示

    print("生成的插图信息:", final_result)
    return final_result  # 返回结果的 JSON 文本

def create_course_script(topic_name, level_of_explanation, age, creativity_level, humour_level):
    """
    根据主题、解释水平、年龄以及创意和幽默水平生成对应的课程脚本。

    :param topic_name: str，课程主题的名称。
    :param level_of_explanation: str，决定解释的复杂程度，值可以为 '初学者', '中级', '高级'。
    :param age: int，授课对象的年龄。
    :param creativity_level: int，创意的级别（1-10），决定讲解中使用创造性元素的程度。
    :param humour_level: int，幽默的级别（1-10），决定讲解中包含幽默的程度。

    :return: str，生成的课程脚本内容，格式化为Markdown。
    """

    # 确保年龄为字符串类型，以便用作提示中的描述
    age_str = str(age)

    # 定义各级别的解释方式
    explanation_levels = {
        '初学者': "用简单易懂的方式解释，帮助读者理解直觉、逻辑和重要性。",
        '中级': "以更复杂和深入的方式解释，假设读者有一些先前知识和理解。",
        '高级': "深入讲解主题细节，假设读者有扎实基础，熟悉中级概念。"
    }

    # 获取对应级别的解释方式，若未提供则默认为'初学者'
    level_string = explanation_levels.get(level_of_explanation, explanation_levels['初学者'])

    # 确定创意和幽默的描述
    creativity_string = (
        "" if creativity_level < 4
        else "在解释时富有创意，以便于理解。" if creativity_level < 7
        else "在解释时富有创意并使用类比。"
    )

    humour_string = (
        "" if humour_level < 4
        else "在解释时带一点幽默。" if humour_level < 7
        else "在解释时增添笑话，使学习更有趣。"
    )

    # 构造提示词，综合所有参数生成课程内容的描述
    prompt = (
        f"请生成一个关于{topic_name}的课程内容。"
        f"授课对象为：{age_str}岁的学生。"
        f"解释关键概念。"
        f"课程内容要求：{level_string}{creativity_string}{humour_string}。"
        f"输出应为Markdown格式。"
        f"只生成 # 和 ## 两层目录结构，其他都作为要点内容"
        f"除了课程内容本身，不要输出其他内容"
    )

    print('prompt=', prompt)

    # 直接调用 API 获取课程脚本
    completion = client.chat.completions.create(
        model="qwen-max",
        messages=[
            {"role": "system", "content": "你是一名专注于科学普及的课程讲师，负责为学生提供清晰的课程内容"},
            {"role": "user", "content": prompt},
        ],
        stream=True
    )

    # 解析 API 返回结果，提取生成的课程脚本内容
    generated_content = ""
    for chunk in completion:
        chunk_content = chunk.choices[0].delta.content
        generated_content += chunk_content
        print(chunk_content, end="")

    # 返回生成的课程脚本内容
    return generated_content


def create_illustration_prompt(script):
    """
    根据给定的脚本生成插图提示，并通过 API 获取详细插图信息。

    :param script: str，微课程的主题或内容脚本，用于生成插图提示。

    :return: str，生成的插图提示，包含详细的插图信息和格式的 JSON 字符串。
    """

    # 系统消息，指定模型的角色和任务
    system_message = (
        "您是一位插图提示生成专家，专注于为微课程生成详细的插图提示。"
    )

    # 构建插图生成提示
    prompt = f"""生成关于 {script} 的插图。返回仅包含多个插图详细信息的 JSON 数组。
    ### 改进指南:
    - **主题:** 中心概念。
    - **描述:** 详细叙述重点元素、情感和氛围。
    - **场景:** 特定环境（如自然、城市、太空），包括颜色、光线和情绪。
    - **对象:** 主要主题和特征（如人、动物、物体）。
    - **动作:** 对象的动态（如飞行、跳跃、闲逛）。
    - **风格:** 艺术技巧（如抽象、超现实主义、水彩、矢量）。
    - **细节:** 其他特定信息（如纹理、背景元素）。
    ### 生成的提示结构:
    “描述, 场景, 包含对象, 动作. 以风格呈现, 强调细节。”
    ### 提示示例
    [
        {{
            "插图编号": 1,
            "插图位置": "第一课",
            "图片标题": "阳光明媚的日子",
            "插图描述": "一幅富有创意的数字艺术作品，描绘了一只由埃菲尔铁塔构建的长颈鹿。"
        }},
        {{
            "插图编号": 2,
            "插图位置": "第二课",
            "图片标题": "繁星之夜",
            "插图描述": "一幅黑暗幻想肖像，呈现了一匹马奔跑在风暴中，背景火焰般的景观。"
        }}
    ]
    输出格式为JSON。不包含任何额外的文字、解释或评论，必须包含所有的4个要素。"""

    # 调用 API 获取插图数据
    completion = client.chat.completions.create(
        model="qwen-max",
        messages=[
            {"role": "system", "content": system_message},
            {"role": "user", "content": prompt},
        ],
        stream=True
    )

    # 解析 API 返回结果
    generated_content = ""
    for chunk in completion:
        chunk_content = chunk.choices[0].delta.content
        generated_content += chunk_content
        print(chunk_content, end="")

    print("生成的插画提示词:", generated_content)  # 打印生成的提示

    # 返回生成的插图提示
    return generated_content

def add_illustration_to_course_script(script, course_script_with_illustrations_file_path, illustrations):
    """
    将插图整合到课程脚本中，并返回带插图的Markdown格式课程脚本。

    参数：
    - script: str，原始课程脚本，包含文本内容。
    - course_script_with_illustrations_file_path: str，保存生成课程脚本的文件路径，用于提供上下文。
    - illustrations: list，插图列表，包含要插入的图像信息。

    返回：
    - str，更新后的带插图的课程脚本，格式为Markdown。
    """

    # 创建系统消息，指示助手的角色和任务
    system_message = "你是一名助手，专注于将插画插入课程脚本中。"

    # 计算插图目录的路径，以便后续在提示词中使用
    illustrations_dir = "../illustrations/"

    # 构建提示词，要求模型在指定位置整合插图
    prompt = (
        "请在相关位置将插图整合到课程脚本中。"
        "输出的带插画的课程脚本保持Markdown格式。"
        f"根据文件路径 {course_script_with_illustrations_file_path}，"
        f"请将插图的相对路径调整为 {illustrations_dir}"
        "。请勿包含任何额外的解释。\n\n"
        f"课程脚本:\n{script}\n\n"
        f"插图列表:\n{illustrations}"
    )

    # 调用 API 获取插图数据，与模型进行交互以生成带插图的课程脚本
    completion = client.chat.completions.create(
        model="qwen-max",
        messages=[
            {"role": "system", "content": system_message},
            {"role": "user", "content": prompt},
        ],
        stream=True
    )

    # 解析 API 返回的结果，确保可以读取生成的内容
    generated_content = ""
    print("生成带插画的课程脚本:")
    for chunk in completion:
        chunk_content = chunk.choices[0].delta.content
        generated_content += chunk_content
        print(chunk_content, end="")

    # 返回生成的包含插图的课程脚本
    return generated_content

# 调用 generate_illustrations 函数，生成所有插画并将插画信息保存为 JSON 文件。
# 读取配置文件中的课程插画信息文件路径
# illustrations_info_file_path = project_config["illustrations_info_file_path"].format(title=project_config["title"])
#
# # 读取配置文件中的课程内容信息
# title = project_config["title"]
# level_of_explanation = project_config["level_of_explanation"]
# age = project_config["age"]
# creativity_level = project_config["creativity_level"]
# humour_level = project_config["humour_level"]
# # 调用函数生成课程脚本
# script = create_course_script(title, level_of_explanation, age, creativity_level, humour_level)
#
# # 调用函数生成插画提示词
# illustrations_prompt = create_illustration_prompt(script)
#
# # 生成插图和插画信息
# illustrations_info = generate_illustrations(illustrations_prompt)
#
# # 调用函数保存插画信息
# save_file(illustrations_info, illustrations_info_file_path)
#
# # 读取配置文件中的带插画的课程文件路径
# course_script_with_illustrations_file_path = project_config["course_script_with_illustrations_file_path"].format(title=project_config["title"])
#
# # 生成带插图的课程脚本
# course_script_with_illustrations = add_illustration_to_course_script(script, course_script_with_illustrations_file_path, illustrations_info)
#
# # 调用函数保存带插图的课程脚本
# save_file(course_script_with_illustrations, course_script_with_illustrations_file_path)

def create_initial_translation(source_lang, target_lang, source_text):
    """
    将源文本从指定语言翻译成目标语言，并返回翻译结果。

    参数：
    - source_lang: str，源语言代码（如 'zh' 表示中文）。
    - target_lang: str，目标语言代码（如 'en' 表示英语）。
    - source_text: str，待翻译的源文本。

    返回：
    - str，翻译后的文本。
    """

    # 创建系统消息，指示助手的角色和任务
    system_message = f"您是一位专业翻译，专注于将 {source_lang} 翻译成 {target_lang}。"

    # 将源文本和翻译任务包含在提示中，要求只输出翻译内容
    prompt = f"""您的任务是将以下文本：{source_text}从{source_lang}翻译为{target_lang}。请仅输出翻译内容，无需附加其他信息。图片路径不需要翻译。输出格式为 Markdown。"""

    # 通过 API 调用翻译服务，生成翻译结果
    completion = client.chat.completions.create(
        model="qwen-turbo",
        messages=[
            {"role": "system", "content": system_message},
            {"role": "user", "content": prompt},
        ],
    )

    # 如果 model_dump_json() 返回字符串需要解析为 JSON 格式
    dumped_json = json.loads(completion.model_dump_json())

    # 从 JSON 获取生成的翻译内容
    generated_content = dumped_json['choices'][0]['message']['content']
    print("生成初步翻译:", generated_content)

    # 返回翻译后的文本
    return generated_content

def reflect_on_translation(source_lang, target_lang, source_text, translation, country):
    system_message = (
        f"您是一位语言专家，专注于从 {source_lang} 翻译到 {target_lang}。"
        f"您将获得源文本及其翻译，您的目标是改进翻译内容。"
    )

    prompt = f"""
    您的任务是仔细阅读从 {source_lang} 翻译到 {target_lang} 的源文本和翻译，然后提供建设性的批评和有用的建议，以改善翻译。

    翻译的最终风格和语气应符合在 {country} 口语中使用的 {target_lang} 的风格。

    源文本和翻译如下：
    <SOURCE_TEXT>
    {source_text}
    </SOURCE_TEXT>
    <TRANSLATION>
    {translation}
    </TRANSLATION>

    在写建议时，请注意是否有改善翻译的方式：
    (i) 准确性（通过纠正添加、误译、遗漏或未翻译文本的错误），
    (ii) 流畅性（通过应用 {target_lang} 的语法、拼写和标点规则，确保没有不必要的重复），
    (iii) 风格（确保翻译反映源文本的风格，并考虑任何文化背景），
    (iv) 术语（确保术语使用的一致性，反映源文本的领域；并仅使用与 {target_lang} 等价的习语）。

    写出一系列具体、有帮助和建设性的建议，以改进翻译。
    每个建议应针对翻译的一个具体部分。
    只输出建议，不加入任何额外的解释内容。
    输出格式为 Markdown。
    """

    # 直接调用 API 获取课程脚本
    completion = client.chat.completions.create(
        model="qwen-turbo",
        messages=[
            {"role": "system", "content": system_message},
            {"role": "user", "content": prompt},
        ],
    )

    # 如果 model_dump_json() 返回字符串需要解析为 JSON
    dumped_json = json.loads(completion.model_dump_json())

    generated_content = dumped_json['choices'][0]['message']['content']
    print("生成翻译反思:", generated_content)

    # 返回翻译反思
    return generated_content

def improve_translation(source_lang, target_lang, source_text, initial_translation, reflection):
    """
    改进从源语言翻译到目标语言的文本翻译。

    此函数通过使用初始翻译和改进建议，生成更准确和流畅的翻译文本。
    函数将通过 API 调用与翻译模型进行交互，利用系统消息、源文本、初始翻译和反思建议生成最终翻译。

    参数:
    source_lang (str): 源语言的名称或代码，例如 '中文' 或 'en'。
    target_lang (str): 目标语言的名称或代码，例如 '英文' 或 'fr'。
    source_text (str): 需要翻译的源文本内容。
    initial_translation (str): 初始翻译文本，用于比较和改进。
    reflection (str): 专家的改进建议和建设性批评，供文本编辑参考。

    返回:
    str: 改进后的翻译文本，仅包含新的翻译内容。

    注意:
    函数只输出最终翻译结果，并不附加其他内容。输出格式为 Markdown。
    """

    # 定义系统消息，描述编辑任务
    system_message = (
        f"您是一位高级编辑，专注于从 {source_lang} 翻译到 {target_lang} 的后期编辑。"
        f"您将获得源文本、初始翻译和建议，您的目标是改进翻译内容。"
    )

    # 构建用于请求的提示信息
    prompt = f"""
    您的任务是仔细阅读并编辑从 {source_lang} 翻译到 {target_lang} 的翻译，
    同时考虑专家建议和建设性批评的列表。
    源文本：
    <SOURCE_TEXT>
    {source_text}  # 源文本内容
    </SOURCE_TEXT>
    初始翻译：
    <INITIAL_TRANSLATION>
    {initial_translation}  # 初始翻译内容
    </INITIAL_TRANSLATION>
    改进建议：
    <SUGGESTIONS>
    {reflection}  # 提供的反思或改善建议
    </SUGGESTIONS>

    请在编辑翻译时考虑专家建议。编辑翻译时请确保：
    (i) 准确性（纠正添加、误翻、遗漏或未翻译文本的错误），
    (ii) 流畅性（应用 {target_lang} 的语法、拼写和标点规则，确保没有不必要的重复），
    (iii) 风格（确保翻译反映源文本的风格），
    (iv) 术语（不适合上下文或使用不一致），
    (v) 其他错误。
    只输出新的翻译，不附加其他内容。
    输出格式为Markdown。
    """

    # 直接调用 API 获取课程脚本
    completion = client.chat.completions.create(
        model="qwen-turbo",  # 使用的模型
        messages=[
            {"role": "system", "content": system_message},  # 系统消息
            {"role": "user", "content": prompt},  # 用户提示
        ],
    )

    # 解析从 API 返回的 JSON 数据
    dumped_json = json.loads(completion.model_dump_json())
    # 提取生成的内容
    generated_content = dumped_json['choices'][0]['message']['content']

    # 打印生成的优化后的翻译
    print("生成优化后的翻译:", generated_content)

    # 返回优化后的翻译内容
    return generated_content

# 读取配置文件中的课程脚本文件路径
course_script_with_illustrations_file_path = project_config["course_script_with_illustrations_file_path"].format(title=project_config["title"])

# 1、第一步需要进行初步翻译
# 根据课程脚本文件路径读取课程内容
script = read_text_from_file(course_script_with_illustrations_file_path)

# 读取配置文件中的源语言、目标语言信息
source_lang = project_config["source_lang"]
target_lang = project_config["target_lang"]

# 调用函数进行初步翻译
initial_translation = create_initial_translation(source_lang, target_lang, script)

# 读取配置文件中的初步翻译文件路径
initial_translation_file_path = project_config["initial_translation_file_path"].format(title=project_config["title"])

# 保存初步翻译结果
save_file(initial_translation, initial_translation_file_path)

# 读取配置文件中的国家信息
country = project_config["country"]

# 调用函数进行反思
reflection_on_translation = reflect_on_translation(source_lang, target_lang, script, initial_translation, country)

# 读取配置文件中的翻译反思文件路径
reflection_on_translation_file_path = project_config["reflection_on_translation_file_path"].format(title=project_config["title"])

# 保存翻译反思结果
save_file(reflection_on_translation, reflection_on_translation_file_path)

# 调用函数进行反思
improved_translation = improve_translation(source_lang, target_lang, script, initial_translation, reflection_on_translation)

# 读取配置文件中的优化翻译文件路径
improved_translation_file_path = project_config["improved_translation_file_path"].format(title=project_config["title"])

# 保存优化后的翻译结果
save_file(improved_translation, improved_translation_file_path)